﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace TC
{
    public interface IUpdate
    {
        void OnUpdate(float delta);
    }

    public enum VarType
    {
        Int,
        Float,
        Long,
        String,
        Bool,
        Vector3,
        GameObject,
        Blurprint,
        IntArray,
        FloatArray,
        LongArray,
        StringArray,
        BoolArray,
        Vector3Array,
        GameObjectArray
    }

    public class ValueWrap
    {
        public string name;
        public object value;
        public VarType type;
    }

    public abstract class Graph : ISerialize
    {
        public string name;
        public Context context;
        
        public List<Node> nodes = new List<Node>();
        public List<PortConnection> connections = new List<PortConnection>();
        public List<ValueWrap> vars = new List<ValueWrap>();//参数

        public Dispatcher dispatcher;//主调用栈

        public Graph(Context context, Dispatcher dispatcher)
        {
            this.context = context;
            this.dispatcher = dispatcher;
        }

        #region virtual
        public virtual void OnCreate()
        {
            foreach (var item in nodes)
            {
                item.OnCreate();
            }
        }

        public void OnUpdate()
        {
            dispatcher.Update(1 / 60.0f);
        }

        public virtual void OnDestroy()
        {
            dispatcher.Destroy();

            foreach (var item in nodes)
            {
                item.OnDestroy();
            }
        }

        public void OnSerialize_t(ISerializeEntity entity)
        {
        }

        public void OnDeserialize_t(ISerializeEntity entity)
        {
        }

        public ISerializeEntity CreateEntity()
        {
            return null;
        }

        #endregion

        #region node
        public T AddNode<T>() where T : Node, new()
        {
            T node = new T();
            node.graph = this;
            node.OnCreate();
            nodes.Add(node);

            return node;
        }

        public void AddNode(Node node)
        {
            node.OnCreate();
            nodes.Add(node);
        }

        public void RemoveNode(Node node)
        {
            List<PortConnection> ls = new List<PortConnection>();
            foreach (var i in connections)
            {
                if (i.source.parent == node
                    || i.target.parent == node)
                {
                    ls.Add(i);
                }
            }
            foreach (var i in ls)
            {
                connections.Remove(i);
            }

            node.OnDestroy();
            nodes.Remove(node);
        }
        #endregion

        #region port
        public T GetConnectPort<T>(ValuePort p) where T : ValuePort
        {
            Port o = null;
            foreach (var i in connections)
            {
                o = i.GetOther(p);
                if (o != null)
                {
                    return o as T;
                }
            }
            return null;
        }

        public Port GetConnect(Port p)
        {
            foreach (var i in connections)
            {
                if (i.source == p)
                    return i.target;
                else if (i.target == p)
                    return i.source;
            }
            return null;
        }

        public bool HasConnect(Port p)
        {
            foreach (var i in connections)
            {
                if (i.source == p
                    || i.target == p)
                    return true;
            }
            return false;
        }
        #endregion

        #region connect
        public FlowConnection AddConnection(FlowPort p0, FlowPort p1)
        {
#if UNITY_EDITOR
            Debug.Assert(p0.IsInput != p1.IsInput);
#endif
            if(p0.CanConnect(p1))
            {
                FlowConnection connect = null;
                if (p0.IsInput)
                {
                    connect = new FlowConnection(p1, p0);
                }
                else
                {
                    connect = new FlowConnection(p0, p1);
                }
                connections.Add(connect);
                return connect;
            }
            return null;
        }

        public ValueConnection AddConnection(ValuePort p0, ValuePort p1)
        {
#if UNITY_EDITOR
            Debug.Assert(p0.IsInput != p1.IsInput);
#endif
            if (p0.CanConnect(p1))
            {
                ValueConnection connect = null;
                if (p0.IsInput)
                {
                    connect = new ValueConnection(p1, p0);
                }
                else
                {
                    connect = new ValueConnection(p0, p1);
                }
                connect = new ValueConnection(p0, p1);
                connections.Add(connect);
                return connect;
            }
            return null;
        }
        
        public void RemoveConnection(Port p0, Port p1)
        {
            for (int i = 0; i < connections.Count; ++i)
            {
                if (connections[i].GetOther(p0) == p1)
                {
                    connections.RemoveAt(i);
                }
            }
        }

        public void RemoveConnection(PortConnection connect)
        {
            connections.Remove(connect);
        }
        #endregion
    }
}